package com.cayden.commons.httpclient;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Set;

/**
 *
 * @author: cayden
 * Date: 2022/2/20 15:14
 * http 默认请求 Get
 */
public class HttpClientExecutor extends HttpRequestBase{

    protected static HttpsTrustManager httpsTrustManager = new HttpsTrustManager();

    /**
     * 通过此方法构造出来的的就是Get方法
     * @param url
     */
    public HttpClientExecutor(String url){
        this.url = url;
    }

    public <T extends HttpClientExecutor> T maxConnectionSeconds(int maxConnectionSeconds){
        this.maxConnectionSeconds = maxConnectionSeconds;
        return (T) this;
    }

    public <T extends HttpClientExecutor> T addRequestParam(String key, String value){
        this.requestParams.put(key,value);
        return (T) this;
    }

    public <T extends  HttpClientExecutor> T addRequestHeader(String key, String value){
        this.headers.put(key,value);
        return (T) this;
    }

    public <T extends HttpClientExecutor> T contentType(String contentType){
        this.contentType = contentType;
        return (T) this;
    }

    public <T extends HttpClientExecutor> T proxyHost(HttpHost proxyHost){
        this.proxyHost = proxyHost;
        return (T) this;
    }

    public <T extends HttpClientExecutor> T httpEntity(HttpEntity httpEntity){
        this.httpEntity = httpEntity;
        return (T) this;
    }

    public <T extends HttpClientExecutor> T mobileRequest(){
        this.addRequestHeader("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36");
        return (T) this;
    }

    public <T> T execute(HttpResponseHandler<T> responseHandler){
        T result = null;
        try {
            final CloseableHttpResponse response = sendRequest();
            result = responseHandler.handleResponse(new CaydenHttpResponse(response));
            response.close();
        }catch (Exception e){
            LOG.error("send request failed,this url is :" + url,e);
        }
        return result;
    }

    private CloseableHttpResponse sendRequest() throws Exception{
        HttpUriRequest request = retrieveHttpRequest();
        setContentType(request);
        setHeaders(request);
        CloseableHttpClient client = retrieveHttpClient();
        // 交由上层处理
        return client.execute(request);
    }

    protected CloseableHttpClient retrieveHttpClient() {
        final RequestConfig requestConfig = requestConfig();
        if (isHttps()){
            // SSL support
            SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(createSSLContext(),new NoopHostnameVerifier());
            return HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
        }else {
            return HttpClients.custom().setDefaultRequestConfig(requestConfig).build();
        }
    }

    private SSLContext createSSLContext() {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null,new HttpsTrustManager[]{httpsTrustManager},null);
            return sslContext;
        }catch (Exception e){
            throw new IllegalStateException("create SSLContext error",e);
        }
    }

    protected RequestConfig requestConfig() {
        final int maxConnMillSeconds = this.maxConnectionSeconds * MS_TO_S_UNIT;
        final RequestConfig.Builder build = RequestConfig.custom()
                .setSocketTimeout(maxConnMillSeconds)
                .setConnectTimeout(maxConnMillSeconds);
        setProxy(build);
        return build.build();
    }

    protected void setProxy(RequestConfig.Builder build) {
        if (this.proxyHost != null){
            build.setProxy(this.proxyHost);
        }
    }

    protected boolean isHttps(){
        return url.toLowerCase().startsWith(HTTPS);
    }

    protected HttpUriRequest retrieveHttpRequest() {
        final RequestBuilder builder = createRequestBuilder();
        addRequestParam(builder);
        setHttpEntity(builder);
        return builder.setUri(url).build();
    }

    protected void setHttpEntity(RequestBuilder builder) {
        if (this.httpEntity != null){
            builder.setEntity(this.httpEntity);
        }
    }

    protected void addRequestParam(RequestBuilder builder){
        final Set<String> params = requestParams.keySet();
        for (String key : params){
            builder.addParameter(key,requestParams.get(key));
        }
    }

    protected void setContentType(HttpUriRequest request) {
        if (StringUtils.hasText(this.contentType)){
            request.setHeader(HttpHeaders.CONTENT_TYPE,contentType);
            LOG.debug("Set HttpUriRequest[{}] content type :{}",request,contentType);
        }
    }

    protected void setHeaders(HttpUriRequest request){
        for (String header : headers.keySet()){
            request.addHeader(header,headers.get(header));
        }
    }

    /**
     *
     * @return request builder
     */
    protected RequestBuilder createRequestBuilder(){
        return RequestBuilder.get();
    }

    private static class HttpsTrustManager implements X509TrustManager{

        @Override
        public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            // ignore
        }

        @Override
        public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            // ignore
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    }

}
